Skip to content

deploy: 휴면 스케줄러, GitHub OAuth, 예외 처리 리팩토링, 테스트 코드 dev-deploy 반영 - #35

Closed
Jungeunsun565 wants to merge 22 commits into
dev-deployfrom
dev
Closed

deploy: 휴면 스케줄러, GitHub OAuth, 예외 처리 리팩토링, 테스트 코드 dev-deploy 반영#35
Jungeunsun565 wants to merge 22 commits into
dev-deployfrom
dev

Conversation

@Jungeunsun565

@Jungeunsun565 Jungeunsun565 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🔀 PR 개요

dev 브랜치에 누적된 아래 기능/개선 사항을 dev-deploy에 반영합니다.

  • 휴면 전환, 계정 하드 삭제 스케줄러 구현 (Redis 분산 락 적용)
  • GitHub OAuth 로그인 추가
  • 전역 예외 처리(BusinessException/GlobalExceptionHandler) 도입
  • 컨트롤러 응답 구조 단순화 (ApiResponse 제거, DTO 직접 반환)
  • HeaderAuthenticationFilter가 X-User-Role 헤더 기반으로 동작하도록 변경
  • User/UserCredential/UserRole/Oauth 엔티티 및 Repository 계층 테스트 작성

📄 변경 사항

어떤 부분이 수정/추가/삭제되었는지 구체적으로 기술해 주세요.

  • 새로운 기능 추가 (✨ Feature)
  • 버그 수정 (🐞 BugFix)
  • 코드 리팩토링 (🔧 Refactor)
  • 문서 수정 (📝 Docs)
  • 테스트 코드 추가 (🧪 Test)
  • 배포 관련 (🚀 Deploy)
  • 기타 설정 변경 (🧰 Setting)

💡 상세 내용

Feature

  • 휴면 전환/계정 하드 삭제 스케줄러 (이중화 환경 대비 Redis 분산 락 적용)
  • GitHub OAuth 로그인 (OauthClientResolver로 provider별 클라이언트 분리)
  • BusinessException 기반 전역 예외 처리기

Fix

  • GitHub OAuth 반영에 따른 MyPageServiceImpl 수정
  • 불필요한 이메일 주소 제거 및 FeignClient 어노테이션 수정
  • Feign Circuit Breaker ID 하이픈 제거 방지 설정 추가

Refactor

  • Service 계층 조회 메서드에 readOnly 트랜잭션 적용
  • 컨트롤러 응답에서 ApiResponse 제거, DTO 직접 반환
  • HeaderAuthenticationFilter가 X-User-Role 헤더 사용하도록 변경

Test

  • User, UserCredential, UserRole, Oauth 엔티티 테스트
  • UserRepository, UserRoleRepository, UserCredentialRepository, OauthRepository 테스트

🧩 관련 이슈


🧪 테스트 방법

변경 내용을 확인할 수 있는 방법을 단계별로 설명해 주세요.
코드 블록(```)으로 콘솔 로그나 테스트 코드 결과를 첨부해도 좋습니다.

예시:

# 로컬 서버 실행
npm run dev
# 또는
python manage.py runserver


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **새로운 기능**
  * Google 및 GitHub OAuth 로그인을 지원합니다.
  * 사용자 정보 조회와 관리자 그룹 확인 기능을 추가했습니다.
  * 탈퇴 사용자 영구 삭제 및 장기 미접속 사용자 휴면 전환을 자동화했습니다.
  * OAuth 연동 실패와 외부 서비스 장애를 명확한 오류로 안내합니다.

* **개선 사항**
  * API 응답 형식을 간소화해 데이터를 직접 반환합니다.
  * 인증 헤더의 역할 정보를 안정적으로 처리합니다.
  * 비즈니스 오류별 HTTP 상태와 메시지를 일관되게 제공합니다.

* **테스트**
  * 사용자, OAuth, 인증 정보 및 역할 관련 기능의 검증을 강화했습니다.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Jungeunsun565 and others added 16 commits August 6, 2026 21:38
feat: GitHub OAuth 로그인 추가 및 트랜잭션 리팩토링

- GitHub/Google OAuth 로그인 구현
- 트랜잭션 처리 리팩토링
- 불필요한 이메일 주소 제거
- FeignClient 어노테이션 수정
refactor: 전역 예외 처리 및 응답 구조 리팩토링
fix: Feign Circuit Breaker ID의 하이픈 제거 방지 설정 추가
…r-filter

refactor: HeaderAuthenticationFilter X-User-Role 헤더 사용
test: User, UserCredential, UserRole, Oauth 엔티티 테스트 작성
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jungeunsun565, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d4163fc8-c036-456a-b443-a2bea346c155

📥 Commits

Reviewing files that changed from the base of the PR and between 5ec222e and 6aef4f3.

📒 Files selected for processing (10)
  • Dockerfile
  • src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/entity/User.java
  • src/main/java/com/nhnacademy/insightonauth/exception/EmailAlreadyRegisteredException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/ErrorCode.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserSleepConversionScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java
  • src/test/java/com/nhnacademy/insightonauth/repository/UserRepositoryTest.java
  • src/test/resources/application.properties
📝 Walkthrough

Walkthrough

인증 서비스에 Google·GitHub OAuth, Core 서비스 연동, 직접 응답 반환, 전역 예외 처리, 헤더 기반 권한 인증, Redis 분산 잠금 스케줄러를 추가했습니다. 엔터티와 저장소 테스트도 추가했습니다.

Changes

인증 및 사용자 수명주기

Layer / File(s) Summary
OAuth 제공자 및 외부 연동
pom.xml, src/main/java/com/nhnacademy/insightonauth/client/*, src/main/java/com/nhnacademy/insightonauth/client/impl/*, src/main/java/com/nhnacademy/insightonauth/dto/core/*, src/main/resources/application-dev.properties
Feign, LoadBalancer, Resilience4j, Redisson 의존성을 추가했습니다. Google·GitHub OAuth 클라이언트와 provider resolver를 추가했습니다. Core 관리자 그룹 조회 Feign API를 추가했습니다.
응답 및 예외 계약
src/main/java/com/nhnacademy/insightonauth/controller/*, src/main/java/com/nhnacademy/insightonauth/exception/*, src/main/java/com/nhnacademy/insightonauth/handler/*, src/main/java/com/nhnacademy/insightonauth/filter/*
컨트롤러 응답에서 ApiResponse 래퍼를 제거했습니다. 비즈니스 오류 코드와 전역 오류 응답을 추가했습니다. X-User-Role 헤더에서 권한을 생성하도록 인증 필터를 변경했습니다.
서비스 인증 및 Core 검증
src/main/java/com/nhnacademy/insightonauth/service/*, src/main/java/com/nhnacademy/insightonauth/entity/User.java
OAuth resolver를 서비스에 연결했습니다. 로그인 시각 갱신을 엔터티 메서드로 변경했습니다. 회원 탈퇴 전에 Core 서비스의 관리자 그룹 존재 여부를 확인합니다. 조회 서비스에 읽기 전용 트랜잭션을 추가했습니다.
사용자 수명주기 스케줄러
src/main/java/com/nhnacademy/insightonauth/scheduler/*, src/main/java/com/nhnacademy/insightonauth/repository/UserRepository.java, src/main/java/com/nhnacademy/insightonauth/redis/RedisKey.java
매일 실행되는 탈퇴 사용자 물리 삭제와 비활성 사용자 휴면 전환 작업을 추가했습니다. 두 작업은 Redisson 분산 잠금을 사용합니다.
엔터티 및 저장소 검증
src/test/java/com/nhnacademy/insightonauth/entity/*, src/test/java/com/nhnacademy/insightonauth/repository/*, src/test/resources/application.properties
엔터티 생성·상태 변경과 OAuth·인증 정보·사용자·역할 저장소 조회를 검증하는 테스트를 추가했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant UserController
  participant UserServiceImpl
  participant OauthClientResolver
  participant GithubOauthClient
  participant CoreClient
  UserController->>UserServiceImpl: OAuth provider와 code 전달
  UserServiceImpl->>OauthClientResolver: provider별 클라이언트 조회
  OauthClientResolver->>GithubOauthClient: code로 사용자 정보 조회
  GithubOauthClient-->>UserServiceImpl: OauthUserInfo 반환
  UserController->>UserServiceImpl: 회원 탈퇴 요청
  UserServiceImpl->>CoreClient: 관리자 그룹 존재 여부 조회
  CoreClient-->>UserServiceImpl: ManagerGroupExistsResponse 반환
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 휴면 스케줄러, GitHub OAuth, 예외 처리, 테스트 변경과 dev-deploy 반영 목적을 명확하게 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java`:
- Around line 90-94: Update GithubOauthClient’s email selection to require both
primary and verified to be true, rejecting authentication or linking when no
such email exists. In
src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java
lines 90-94, apply this filter before selecting the email. In
src/main/java/com/nhnacademy/insightonauth/client/impl/GoogleOauthClient.java
lines 70-74, validate email_verified is true before constructing OauthUserInfo
and reject the request otherwise.
- Line 30: Update the RestClient initialization in GithubOauthClient to
configure a 3-second connection timeout and a 5-second read timeout, matching
the timeout setup used by GoogleOauthClient for GitHub token, user, and email
requests.

In `@src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java`:
- Around line 3-6: ErrorResponse에 외부 응답용 안정적인 ErrorCode 필드를 추가하고,
src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java:3-6의
생성자·직렬화 구조를 이에 맞게 갱신하세요.
src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java:19-21의
handleBusinessException()에서 BusinessException.getErrorCode()를 새 응답 필드로 매핑해
클라이언트가 오류 코드를 받을 수 있도록 수정하세요.

In `@src/main/java/com/nhnacademy/insightonauth/redis/RedisService.java`:
- Around line 32-34: RedisService.java:32-34의 RedisService.setIfAbsent와 락 해제
API를 확장해 락 획득 시 고유 토큰을 저장하고, 동일 토큰일 때만 원자적으로 삭제하는 compare-and-delete를 구현하세요.
UserHardDeleteScheduler.java:25-46의 UserHardDeleteScheduler와
UserSleepConversionScheduler.java:25-48의 UserSleepConversionScheduler에서는
"locked" 대신 인스턴스별 고유 토큰을 사용하고, finally에서 해당 토큰을 전달해 조건부 해제 API를 호출하세요.

In
`@src/main/java/com/nhnacademy/insightonauth/scheduler/UserSleepConversionScheduler.java`:
- Line 23: Update the `@Scheduled` declaration in UserSleepConversionScheduler so
the cron expression and its Korean time comment describe the same execution
time; either change the cron to run at 04:00 or revise the comment to 02:00,
preserving the intended schedule.

In
`@src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java`:
- Around line 334-335: OAuth 로그인 흐름에서 OauthClient.getUserInfo 이후 토큰을 발급하기 전에
신규·기존 사용자 모두의 lastLoginAt을 현재 시각으로 갱신하도록 UserServiceImpl의 관련 분기 로직을 수정하세요. 사용자
저장까지 완료한 뒤 토큰 발급이 진행되도록 보장하고, 두 OAuth 로그인 경로의 lastLoginAt 갱신을 검증하는 테스트를 추가하세요.

In `@src/main/resources/application-dev.properties`:
- Around line 92-100: Update the Resilience4j circuit-breaker instance keys in
src/main/resources/application-dev.properties:92-100 and
src/test/resources/application.properties:91-95 to match the actual method-based
Feign IDs generated for CoreClient, such as the ID for existsManagerGroup(String
userId), while preserving the shared alphanumeric-ids.enabled=false policy in
both profiles. Replace the ineffective insighton-core key consistently at both
sites so the sliding-window, failure-rate, and wait-duration settings are
applied.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a3b7453-95cb-4b30-a4eb-22a370a65e85

📥 Commits

Reviewing files that changed from the base of the PR and between b970af9 and 3f2a170.

📒 Files selected for processing (66)
  • pom.xml
  • src/main/java/com/nhnacademy/insightonauth/InsightonAuthApplication.java
  • src/main/java/com/nhnacademy/insightonauth/client/CoreClient.java
  • src/main/java/com/nhnacademy/insightonauth/client/OauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/client/OauthClientResolver.java
  • src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/client/impl/GoogleOauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/controller/AdminController.java
  • src/main/java/com/nhnacademy/insightonauth/controller/CoreController.java
  • src/main/java/com/nhnacademy/insightonauth/controller/MypageController.java
  • src/main/java/com/nhnacademy/insightonauth/controller/UserController.java
  • src/main/java/com/nhnacademy/insightonauth/dto/ApiResponse.java
  • src/main/java/com/nhnacademy/insightonauth/dto/ErrorResponse.java
  • src/main/java/com/nhnacademy/insightonauth/dto/core/AuthUserResponse.java
  • src/main/java/com/nhnacademy/insightonauth/dto/core/ManagerGroupExistsResponse.java
  • src/main/java/com/nhnacademy/insightonauth/email/EmailService.java
  • src/main/java/com/nhnacademy/insightonauth/exception/BusinessException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/CoreServiceUnavailableException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/DuplicateEmailException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/DuplicatePhoneNumberException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/EmailNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/EmailSendException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/ErrorCode.java
  • src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidCredentialsException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidReactiveTokenException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidRefreshTokenException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidUserException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidUserStatusException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidVerificationCodeException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/InvalidVerificationTokenException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/LastLoginMethodException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/LoginTemporarilyLockedException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/ManagerGroupExistsException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/OauthAlreadyLinkedException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/OauthNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/RefreshTokenNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/RestorePeriodExpiredException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/UnsupportedOAuthProviderException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/UserCredentialsNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/UserNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/UserRoleNotFoundException.java
  • src/main/java/com/nhnacademy/insightonauth/exception/VerificationTemporarilyLockedException.java
  • src/main/java/com/nhnacademy/insightonauth/filter/HeaderAuthenticationFilter.java
  • src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java
  • src/main/java/com/nhnacademy/insightonauth/redis/RedisKey.java
  • src/main/java/com/nhnacademy/insightonauth/redis/RedisService.java
  • src/main/java/com/nhnacademy/insightonauth/repository/UserRepository.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserSleepConversionScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/service/UserService.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/MyPageServiceImpl.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/OauthServiceImpl.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/UserCredentialServiceImpl.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/UserRoleServiceImpl.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java
  • src/main/resources/application-dev.properties
  • src/test/java/com/nhnacademy/insightonauth/entity/OauthTest.java
  • src/test/java/com/nhnacademy/insightonauth/entity/UserCredentialTest.java
  • src/test/java/com/nhnacademy/insightonauth/entity/UserRoleTest.java
  • src/test/java/com/nhnacademy/insightonauth/entity/UserTest.java
  • src/test/java/com/nhnacademy/insightonauth/repository/OauthRepositoryTest.java
  • src/test/java/com/nhnacademy/insightonauth/repository/UserCredentialRepositoryTest.java
  • src/test/java/com/nhnacademy/insightonauth/repository/UserRepositoryTest.java
  • src/test/java/com/nhnacademy/insightonauth/repository/UserRoleRepositoryTest.java
  • src/test/resources/application.properties
💤 Files with no reviewable changes (3)
  • src/main/java/com/nhnacademy/insightonauth/dto/ApiResponse.java
  • src/main/java/com/nhnacademy/insightonauth/dto/ErrorResponse.java
  • src/main/java/com/nhnacademy/insightonauth/email/EmailService.java

Comment thread src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java Outdated
Comment on lines +3 to +6
public record ErrorResponse(
int status,
String message
) {

@coderabbitai coderabbitai Bot Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)ErrorResponse\.java$|GlobalExceptionHandler\.java$|BusinessException\.java$|ErrorCode\.java$|.*\.md$|.*\.adoc$|.*\.json$|.*\.yml$|.*\.yaml$' || true

echo "== ErrorResponse =="
if [ -f src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java ]; then
  cat -n src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java
fi

echo "== GlobalExceptionHandler =="
if [ -f src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java ]; then
  cat -n src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java
fi

echo "== search responses and error code contracts =="
rg -n "ErrorResponse|errorCode|errorCodeValue|ErrorCode|BusinessException|error\\b|code|message" -S src README* docs .github 2>/dev/null || true

Repository: nhnacademy-aiot3-insighton/InsightOn-auth

Length of output: 33915


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== BusinessException =="
cat -n src/main/java/com/nhnacademy/insightonauth/exception/BusinessException.java

echo "== ErrorCode =="
cat -n src/main/java/com/nhnacademy/insightonauth/exception/ErrorCode.java

echo "== Java record field order/record method resolution behavior probe =="
cat > /tmp/RecordBehaviorProbe.java <<'JAVA'
enum TestStatus {
  OK("OK");
  private final String status;
  TestStatus(String status) { this.status = status; }
  public String status() { return status; }
}

record ErrorResponse(int status, String message) {
  public String status() {
    return "OVERRIDDEN";
  }
}

public class RecordBehaviorProbe {
  public static void main(String[] args) {
    ErrorResponse r = new ErrorResponse(400, "msg");
    System.out.println("status field: " + r.status());
    System.out.println("status: " + r.status());
  }
}
JAVA

javac /tmp/RecordBehaviorProbe.java 2>&1 || true
if [ -f /tmp/RecordBehaviorProbe.class ]; then
  java -cp /tmp RecordBehaviorProbe
fi

echo "== record field / method order consistency in ErrorResponse =="
python3 - <<'PY'
from pathlib import Path
s = Path('src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java').read_text()
print(s)
print("contains ErrorCode parameter:", "ErrorCode" in s)
print("has overriding status() in ErrorResponse:", "ErrorResponse" in s and "public String status()" in s)
PY

Repository: nhnacademy-aiot3-insighton/InsightOn-auth

Length of output: 3003


오류 응답에 ErrorCode를 포함하세요. ErrorResponsestatus 필드만 유지하고, handleBusinessException()getErrorCode()를 직렬화하지 않아 클라이언트가 BusinessException의 안정적인 오류 코드를 처리할 수 없습니다. 외부 응답에 안정적인 코드 필드를 추가하고 비즈니스 예외가 이를 매핑하도록 수정하세요.

📍 Affects 2 files
  • src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java#L3-L6 (this comment)
  • src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java#L19-L21
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java`
around lines 3 - 6, ErrorResponse에 외부 응답용 안정적인 ErrorCode 필드를 추가하고,
src/main/java/com/nhnacademy/insightonauth/exception/ErrorResponse.java:3-6의
생성자·직렬화 구조를 이에 맞게 갱신하세요.
src/main/java/com/nhnacademy/insightonauth/handler/GlobalExceptionHandler.java:19-21의
handleBusinessException()에서 BusinessException.getErrorCode()를 새 응답 필드로 매핑해
클라이언트가 오류 코드를 받을 수 있도록 수정하세요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

팀 논의가 필요한 사안이라 이번 PR에서는 보류하고, 별도 이슈로 이어가겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jungeunsun565, 확인했습니다. 이 변경은 현재 PR 범위에서 보류하겠습니다.

후속 GitHub 이슈를 생성할까요?

You are interacting with an AI system.

Comment thread src/main/java/com/nhnacademy/insightonauth/redis/RedisService.java Outdated
Comment thread src/main/resources/application-dev.properties Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java (2)

47-55: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

삭제 성공 건수와 대상 건수를 분리하세요.

Line 50에서 개별 삭제 실패를 처리하지만 Line 55는 실패한 대상까지 포함한 targets.size()를 “완료” 건수로 기록합니다. 부분 실패가 전체 성공으로 보이므로 운영자가 재처리 필요성을 놓칠 수 있습니다. 성공 건수와 실패 건수 또는 실패한 userId 목록을 별도로 기록하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java`
around lines 47 - 55, Update the deletion loop in UserHardDeleteScheduler to
track successful and failed deletions separately: increment a success counter
only after userService.deleteUser completes, and record failures in the existing
catch block. Change the final log so it reports the actual success count
separately from the total targets, with failure information or failed userIds
included.

44-50: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

삭제 직전에 대상 조건을 원자적으로 다시 확인하세요.

Line 45에서 만료 탈퇴 사용자 목록을 조회한 뒤 Line 49에서 ID만 deleteUser()에 전달합니다. deleteUser()는 ID로 사용자를 다시 조회하지만 Status.WITHDRAW와 만료 시점을 검증하지 않습니다. 조회와 삭제 사이에 상태가 변경되면 더 이상 삭제 대상이 아닌 사용자를 물리 삭제할 수 있습니다. 전용 hard-delete 메서드에서 상태·withdrawnAt 조건을 같은 트랜잭션으로 재검증하거나 조건부 삭제를 사용하세요.

PR 목표의 “탈퇴 만료 사용자 삭제” 기능을 기준으로 확인했습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java`
around lines 44 - 50, Update the hard-delete flow in UserHardDeleteScheduler to
revalidate each user’s Status.WITHDRAW and expired withdrawnAt condition
atomically immediately before deletion. Use or introduce a dedicated hard-delete
operation in userService that performs the conditional check and physical delete
within one transaction, replacing the unconditional deleteUser(user.getUserId())
call while preserving per-user exception handling.
src/main/java/com/nhnacademy/insightonauth/entity/User.java (1)

43-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

테스트의 setLastLoginAt(...) 호출을 변경하세요.

UserRepositoryTest.java의 두 호출은 제거된 setter를 참조하므로 컴파일이 실패합니다. 과거 시각을 설정할 수 있는 테스트 경로를 제공하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/nhnacademy/insightonauth/entity/User.java` around lines 43
- 44, UserRepositoryTest.java에서 제거된 setLastLoginAt(...) 두 호출을 지원되는 초기화 경로로 변경하고,
과거 시각의 lastLoginAt 값을 설정할 수 있도록 User 엔티티의 생성자·팩토리·테스트용 설정 경로를 제공하세요. 일반 사용 흐름과
lastLoginAt 필드의 저장 동작은 유지해야 합니다.
src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java (1)

271-278: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Core 호출과 탈퇴 처리를 분리하세요.

UserServiceImpl의 class-level @Transactionalwithdraw()의 데이터베이스 트랜잭션이 coreClient.existsManagerGroup()의 응답까지 유지됩니다. 현재 설정에는 Feign connect/read timeout과 TimeLimiter가 없으며, 회로 차단기 설정만으로 대기 시간을 제한할 수 없습니다. 원격 검증을 non-transactional 경계로 분리하고, 탈퇴 변경은 짧은 별도 트랜잭션에서 처리하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java`
around lines 271 - 278, UserServiceImpl.withdraw() currently holds its database
transaction while waiting for coreClient.existsManagerGroup(). Move the Core
manager-group validation into a non-transactional method or collaborator, then
invoke a separate transactional method for the withdrawal database changes after
validation succeeds. Ensure the remote call is completed before the short
transaction begins.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java`:
- Around line 57-60: Update the unlock handling in UserHardDeleteScheduler so an
IllegalMonitorStateException from lock.unlock() is caught when ownership is lost
between isHeldByCurrentThread() and unlock(). Log the ownership-loss event at
warning level, and prevent the exception from propagating through the finally
block.

In
`@src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java`:
- Around line 352-355: oauthLogin에서 새 User를 저장하기 전에 userRepository로
userInfo.email()의 기존 계정을 조회하도록 수정하세요. 기존 일반 계정 또는 다른 OAuth 계정이 있으면 검증된 이메일과 명시적
연결 정책을 확인한 경우에만 해당 계정에 OAuth 정보를 연결하고, 검증되지 않은 이메일만으로는 자동 연결하지 말고 명확히 거부하세요. 기존
계정이 없을 때만 현재의 User 생성, MEMBER 역할 부여, OAuth 생성 흐름을 수행하세요.
- Around line 352-353: UserServiceImpl의 사용자 생성 흐름에서 userInfo.email()을 직접 사용하지
말고, GitHub OAuth authorization에 user:email scope를 추가한 뒤 모든 경로에서 /user/emails 조회
결과 중 verified이면서 primary인 이메일을 사용하세요. 적합한 이메일이 없으면 User 생성 또는
userRepository.save(newUser) 전에 EmailNotFoundException을 반환하도록 수정하세요.

---

Outside diff comments:
In `@src/main/java/com/nhnacademy/insightonauth/entity/User.java`:
- Around line 43-44: UserRepositoryTest.java에서 제거된 setLastLoginAt(...) 두 호출을
지원되는 초기화 경로로 변경하고, 과거 시각의 lastLoginAt 값을 설정할 수 있도록 User 엔티티의 생성자·팩토리·테스트용 설정 경로를
제공하세요. 일반 사용 흐름과 lastLoginAt 필드의 저장 동작은 유지해야 합니다.

In
`@src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java`:
- Around line 47-55: Update the deletion loop in UserHardDeleteScheduler to
track successful and failed deletions separately: increment a success counter
only after userService.deleteUser completes, and record failures in the existing
catch block. Change the final log so it reports the actual success count
separately from the total targets, with failure information or failed userIds
included.
- Around line 44-50: Update the hard-delete flow in UserHardDeleteScheduler to
revalidate each user’s Status.WITHDRAW and expired withdrawnAt condition
atomically immediately before deletion. Use or introduce a dedicated hard-delete
operation in userService that performs the conditional check and physical delete
within one transaction, replacing the unconditional deleteUser(user.getUserId())
call while preserving per-user exception handling.

In
`@src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java`:
- Around line 271-278: UserServiceImpl.withdraw() currently holds its database
transaction while waiting for coreClient.existsManagerGroup(). Move the Core
manager-group validation into a non-transactional method or collaborator, then
invoke a separate transactional method for the withdrawal database changes after
validation succeeds. Ensure the remote call is completed before the short
transaction begins.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d304952-1dd7-48fd-9094-5e89f1b83a5d

📥 Commits

Reviewing files that changed from the base of the PR and between 3f2a170 and 5ec222e.

📒 Files selected for processing (8)
  • pom.xml
  • src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/entity/User.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserHardDeleteScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserSleepConversionScheduler.java
  • src/main/java/com/nhnacademy/insightonauth/service/UserService.java
  • src/main/java/com/nhnacademy/insightonauth/service/impl/UserServiceImpl.java
  • src/main/resources/application-dev.properties
💤 Files with no reviewable changes (1)
  • src/main/java/com/nhnacademy/insightonauth/service/UserService.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/resources/application-dev.properties
  • src/main/java/com/nhnacademy/insightonauth/client/impl/GithubOauthClient.java
  • src/main/java/com/nhnacademy/insightonauth/scheduler/UserSleepConversionScheduler.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant